/*
 * Copyright (c) xxxx-xxxx, Huan
 * SPDX-License-Identifier: Apache-2.0
 *
 * bilibili UID:179895513
 *
 * Date           Author       Notes
 * 2023-04-28     Huan       the first version
 */
#include "board.h"
#include <rtdevice.h>
#include "g_io_manage.h"
#include "dcmi_drv.h"
#include "ov5640_drv.h"

#define OV5640_I2C_BUS_NAME        "i2c1"
#define OV5640_ADDRESS   0x3C /* OV5640 address */

#define IMAGE_W 240
#define IMAGE_H 200

uint32_t RPG_picture[IMAGE_W*IMAGE_H/2];

struct rt_i2c_bus_device *i2c_bus  = RT_NULL;



/*===========================================================================*/
typedef enum
{
    ON  = 0,
    OFF = 1,
    Reset = 0,
    Set   = 1,
}Pin_state;


static inline void delay_ms(uint8_t ms)
{
    rt_thread_mdelay(ms);
}
static void ov5640_hw_init(void)
{
    G_ioHandle.init_Pout(21); //PB5 RST
    G_ioHandle.high(21);

    G_ioHandle.init_Pout(19); //PB3 POWER
    G_ioHandle.high(19);
}
/* 电源控制 *//* 高:关    低:开 */
static void ov5640_PW(Pin_state state)      //PB3
{
    if(state) G_ioHandle.high(19);
    else      G_ioHandle.low(19);
}
/* 复位引脚控制 *//* 高:置位    低:复位 */
static void ov5640_RST(Pin_state state)     //PB5
{
    if(state) G_ioHandle.high(21);
    else      G_ioHandle.low(21);
}

/* i2c read reg */
static rt_err_t read_reg(struct rt_i2c_bus_device *bus, rt_uint16_t reg, rt_uint8_t len, rt_uint8_t *buf)
{
    struct rt_i2c_msg msg[2] = {0};
    static rt_uint8_t i2c_reg[2] = {0};

    RT_ASSERT(bus != RT_NULL);

    i2c_reg[0] = ((uint16_t)(reg >> 8) & 0xFF);
    i2c_reg[1] = ((uint16_t)(reg & 0xFF));

    msg[0].addr  = OV5640_ADDRESS;
    msg[0].flags = RT_I2C_WR;
    msg[0].buf   = i2c_reg;
    msg[0].len   = 2;

    msg[1].addr  = OV5640_ADDRESS;
    msg[1].flags = RT_I2C_RD;
    msg[1].buf   = buf;
    msg[1].len   = len;

    if (rt_i2c_transfer(bus, msg, 2) == 2)
    {
        return RT_EOK;
    }
    else {
        rt_kprintf("IIC read error\r\n");
    }

    return -RT_ERROR;
}
/* i2c write reg */
static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint16_t reg, rt_uint8_t data)
{
    rt_uint8_t buf[3];
    struct rt_i2c_msg msgs;

    RT_ASSERT(bus != RT_NULL);

    buf[0] = ((uint16_t)(reg >> 8) & 0xFF);
    buf[1] = ((uint16_t)(reg)&0xFF);

    buf[2] = data;

    msgs.addr = OV5640_ADDRESS;
    msgs.flags = RT_I2C_WR;
    msgs.buf = buf;
    msgs.len = 3;

    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }

    return -RT_ERROR;
}
static rt_err_t ov5640_hard_reset(void)
{

    ov5640_RST(Reset);
    delay_ms(50);
    ov5640_PW(ON);
    delay_ms(50);
    ov5640_RST(Set);
    delay_ms(50);

    return RT_EOK;
}
static rt_uint16_t ov5640_read_id(struct rt_i2c_bus_device *bus)
{
    rt_uint8_t read_value[2];
    rt_uint16_t ID;

    read_reg(bus, 0x300A, 1, &read_value[0]);
    read_reg(bus, 0x300B, 1, &read_value[1]);
    ID  = ((uint16_t)(read_value[0] << 8) & 0xFF00);
    ID |= ((uint16_t)(read_value[1]) & 0x00FF);

    if (ID != OV5640_ID)
    {
        rt_kprintf("ov5640 init error, id: 0x%04x\r\n", ID);
        return -RT_ERROR;
    }
    rt_kprintf("ov5640 init success, id: 0x%04x\r\n", ID);

    return RT_EOK;
}

static void ov5640_flash_ctrl(struct rt_i2c_bus_device *bus, rt_uint8_t sw)
{
    write_reg(bus, 0x3016, 0X02);
    write_reg(bus, 0x301C, 0X02);
    if (sw)
    {
        write_reg(bus, 0X3019, 0X02);
    }
    else
    {
        write_reg(bus, 0X3019, 0X00);
    }
}

static rt_err_t ov5640_config(struct rt_i2c_bus_device *bus)
{
    rt_uint32_t i = 0;
    rt_uint8_t value = 0;

    write_reg(bus, 0x3103, 0X11); /* system clock from pad, bit[1] */
    write_reg(bus, 0X3008, 0X82); /* soft reset */

    rt_thread_delay(10);

    for (i = 0; i < (sizeof(RGB565_Init) / 4); i++)
    {
        write_reg(bus, RGB565_Init[i][0], RGB565_Init[i][1]);
//        rt_thread_delay(10);
//        read_reg(bus, RGB565_Init[i][0], 1, &value);
//
//        if (RGB565_Init[i][1] != value)
//        {
//            rt_kprintf("error reg value[0x%x]:0x%02x - 0x%02x", RGB565_Init[i][0], RGB565_Init[i][1], value);
//        }
    }

    ov5640_flash_ctrl(bus, 1); /* open camera flash*/
    rt_thread_delay(3);
    ov5640_flash_ctrl(bus, 0); /* close camera flash*/

    return RT_EOK;
}

/* JPEG */
void ov5640_jpeg_mode(struct rt_i2c_bus_device *bus)
{
    rt_uint16_t i = 0;
    for (i = 0; i < (sizeof(OV5640_jpeg_reg_tbl) / 4); i++)
    {
        write_reg(bus, OV5640_jpeg_reg_tbl[i][0], OV5640_jpeg_reg_tbl[i][1]);
    }
}

/* RGB565 */
void ov5640_rgb565_mode(struct rt_i2c_bus_device *bus)
{
    rt_uint16_t i = 0;
    for (i = 0; i < (sizeof(ov5640_rgb565_reg_tbl) / 4); i++)
    {
        write_reg(bus, ov5640_rgb565_reg_tbl[i][0], ov5640_rgb565_reg_tbl[i][1]);
    }

    write_reg(bus, 0x3821, 0x06);
}

rt_uint8_t ov5640_focus_init(struct rt_i2c_bus_device *bus)
{
    rt_uint16_t tickstart = 0 ,i = 0;
    rt_uint16_t addr = 0x8000;
    rt_uint8_t state = 0x8F;

    write_reg(bus, 0x3000, 0x20); //reset MCU
    for (i = 0; i < sizeof(OV5640_AF_Config); i++)
    {
        write_reg(bus, addr, OV5640_AF_Config[i]);
        addr++;
    }
    write_reg(bus, 0x3022, 0x00);
    write_reg(bus, 0x3023, 0x00);
    write_reg(bus, 0x3024, 0x00);
    write_reg(bus, 0x3025, 0x00);
    write_reg(bus, 0x3026, 0x00);
    write_reg(bus, 0x3027, 0x00);
    write_reg(bus, 0x3028, 0x00);
    write_reg(bus, 0x3029, 0x7f);
    write_reg(bus, 0x3000, 0x00);

    tickstart = rt_tick_get();
    do
    {
        read_reg(bus, 0x3029, 1, &state);
        if (rt_tick_get() - tickstart > 1000)
        {
            return -RT_ERROR;
        }
    } while (state != 0x70);

    return RT_EOK;
}

void ov5640_set_light(struct rt_i2c_bus_device *bus, rt_uint8_t mode)
{
    rt_uint8_t i = 0;
    write_reg(bus, 0x3212, 0x03); //start group 3

    for (i = 0; i < 7; i++)
    {
        write_reg(bus, 0x3400 + i, OV5640_LIGHTMODE_TBL[mode][i]);
    }

    write_reg(bus, 0x3212, 0x13); //end group 3
    write_reg(bus, 0x3212, 0xa3); //launch group 3
}

/* sat:0~6 */
void ov5640_color_saturation(struct rt_i2c_bus_device *bus, rt_uint8_t sat)
{
    rt_uint8_t i = 0;
    write_reg(bus, 0x3212, 0x03); //start group 3
    write_reg(bus, 0x5381, 0x1c);
    write_reg(bus, 0x5382, 0x5a);
    write_reg(bus, 0x5383, 0x06);
    for (i = 0; i < 6; i++)
    {
        write_reg(bus, 0x5384 + i, OV5640_SATURATION_TBL[sat][i]);
    }
    write_reg(bus, 0x538b, 0x98);
    write_reg(bus, 0x538a, 0x01);
    write_reg(bus, 0x3212, 0x13); //end group 3
    write_reg(bus, 0x3212, 0xa3); //launch group 3
}

/* bright:0~8 */
void ov5640_set_brightness(struct rt_i2c_bus_device *bus, rt_uint8_t bright)
{
    rt_uint8_t brtval;
    if (bright < 4)
    {
        brtval = 4 - bright;
    }
    else
    {
        brtval = bright - 4;
    }
    write_reg(bus, 0x3212, 0x03); //start group 3
    write_reg(bus, 0x5587, brtval << 4);
    if (bright < 4)
    {
        write_reg(bus, 0x5588, 0x09);
    }
    else
    {
        write_reg(bus, 0x5588, 0x01);
    }
    write_reg(bus, 0x3212, 0x13); //end group 3
    write_reg(bus, 0x3212, 0xa3); //launch group 3
}

/* contrast:0~6 */
void ov5640_contrast(struct rt_i2c_bus_device *bus, rt_uint8_t contrast)
{
    rt_uint8_t reg0val = 0x00;
    rt_uint8_t reg1val = 0x20;
    switch (contrast)
    {
    case 0:
        reg1val = reg0val = 0X14;
        break;

    case 1:
        reg1val = reg0val = 0X18;
        break;

    case 2:
        reg1val = reg0val = 0X1C;
        break;

    case 4:
        reg0val = 0X10;
        reg1val = 0X24;
        break;

    case 5:
        reg0val = 0X18;
        reg1val = 0X28;
        break;

    case 6:
        reg0val = 0X1C;
        reg1val = 0X2C;

        break;
    }
    write_reg(bus, 0x3212, 0x03); //start group 3
    write_reg(bus, 0x5585, reg0val);
    write_reg(bus, 0x5586, reg1val);
    write_reg(bus, 0x3212, 0x13); //end group 3
    write_reg(bus, 0x3212, 0xa3); //launch group 3
}

/* sharp:0~33 */
void ov5640_set_sharpness(struct rt_i2c_bus_device *bus, rt_uint8_t sharp)
{
    if (sharp < 33)
    {
        write_reg(bus, 0x5308, 0x65);
        write_reg(bus, 0x5302, sharp);
    }
    else
    {
        write_reg(bus, 0x5308, 0x25);
        write_reg(bus, 0x5300, 0x08);
        write_reg(bus, 0x5301, 0x30);
        write_reg(bus, 0x5302, 0x10);
        write_reg(bus, 0x5303, 0x00);
        write_reg(bus, 0x5309, 0x08);
        write_reg(bus, 0x530a, 0x30);
        write_reg(bus, 0x530b, 0x04);
        write_reg(bus, 0x530c, 0x06);
    }
}

rt_uint8_t ov5640_focus_constant(struct rt_i2c_bus_device *bus)
{
    rt_uint8_t temp = 0;
    rt_uint16_t tickstrat = 0;

    write_reg(bus, 0x3023, 0x01);
    write_reg(bus, 0x3022, 0x08);
    do
    {
        tickstrat = rt_tick_get();
        read_reg(bus, 0x3023, 1, &temp);
        if (rt_tick_get() - tickstrat > 1000)
        {
            return -RT_ERROR;
        }
    } while (temp != 0x00);

    write_reg(bus, 0x3023, 0x01);
    write_reg(bus, 0x3022, 0x04);

    do
    {
        tickstrat = rt_tick_get();
        read_reg(bus, 0x3023, 1, &temp);
        if (rt_tick_get() - tickstrat > 1000)
        {
            return -RT_ERROR;
        }
    } while (temp != 0x00);

    return 0;
}

rt_uint8_t ov5640_set_outsize(struct rt_i2c_bus_device *bus, rt_uint16_t offx, rt_uint16_t offy, rt_uint16_t width, rt_uint16_t height)
{
    write_reg(bus, 0X3212, 0X03);

    write_reg(bus, 0x3808, width >> 8);
    write_reg(bus, 0x3809, width & 0xff);
    write_reg(bus, 0x380a, height >> 8);
    write_reg(bus, 0x380b, height & 0xff);

    write_reg(bus, 0x3810, offx >> 8);
    write_reg(bus, 0x3811, offx & 0xff);

    write_reg(bus, 0x3812, offy >> 8);
    write_reg(bus, 0x3813, offy & 0xff);

    write_reg(bus, 0X3212, 0X13);
    write_reg(bus, 0X3212, 0Xa3);

    return RT_EOK;
}




static void OV5640_init(void)
{
    i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(OV5640_I2C_BUS_NAME);
    if (i2c_bus == RT_NULL)
    {
        rt_kprintf("can't find %s device!\n", OV5640_I2C_BUS_NAME);
        return;
    }
    ov5640_hw_init();

    ov5640_hard_reset();
    ov5640_read_id(i2c_bus);
    ov5640_config(i2c_bus);
    ov5640_rgb565_mode(i2c_bus);    /* rgb565 mode */
    ov5640_jpeg_mode(i2c_bus);      /* jpeg mode */
}


#include "dcmi_drv.h"
#include "user_test.h"

static void start_jpg(void)
{
    /* 配置为单次模式，传输一帧后自动停止，等待下次启动 */
    HAL_DCMI_Start_DMA(&dcmi, DCMI_MODE_SNAPSHOT, (uint32_t)RPG_picture, IMAGE_W*IMAGE_H/2);
}
static void jpg_process(void)
{
    HAL_DCMI_Stop(&dcmi);

    Test_Handle.jpg_size = IMAGE_W*IMAGE_H/2 - __HAL_DMA_GET_COUNTER((&dcmi)->DMA_Handle);
    RT_ASSERT(Test_Handle.jpg_size != RT_NULL);

    Test_Handle.jpg_buffer = RPG_picture;
    Test_Handle.state = sample_OK;
}
static int rt_ov5640_init(void)
{
    OV5640_init();

    ov5640_set_outsize(i2c_bus, 4, 0, IMAGE_W, IMAGE_H);

    /* dcmi配置为连续模式，持续传输 */
    //HAL_DCMI_Start_DMA(&dcmi, DCMI_MODE_CONTINUOUS, (uint32_t)RPG_picture, IMAGE_W*IMAGE_H/2);
    Test_Handle.start_jpg   = start_jpg;
    Test_Handle.jpg_process = jpg_process;

    return RT_EOK;
}

#include "rtdef.h"
INIT_APP_EXPORT(rt_ov5640_init);
